﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using WaveEngine.Common.Graphics;
using WaveEngine.Common.Input;
using WaveEngine.Components.Gestures;
using WaveEngine.Components.UI;
using WaveEngine.Extensions.Behaviors;
using WaveEngine.Extensions.ITasks;
using WaveEngine.Framework;
using WaveEngine.Framework.Diagnostic;
using WaveEngine.Framework.Services;
using WaveEngine.Framework.UI;

namespace WaveEngine.Extensions
{
    public class BaseScene : Scene, ITaskContext
    {
        /// <summary>
        /// The tasks
        /// </summary>
        private Dictionary<object, ITask> tasks;

        /// <summary>
        /// The timers
        /// </summary>
        private Dictionary<string, ScopedTimer> timers;

        /// <summary>
        /// The touch manager
        /// </summary>
        private TouchPanel touchManager;

        /// <summary>
        /// Gets the tasks.
        /// </summary>
        /// <value>
        /// The tasks.
        /// </value>
        public IEnumerable<ITask> Tasks
        {
            get { return tasks.Values; }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BaseScene" /> class.
        /// </summary>
        public BaseScene()
            : base()
        {
            this.timers = new Dictionary<string, ScopedTimer>();
            this.touchManager = WaveServices.GetService<TouchPanel>();
            this.tasks = new Dictionary<object, ITask>();
        }

        /// <summary>
        /// Creates the scene.
        /// </summary>
        /// <remarks>
        /// This method is called before all <see cref="T:WaveEngine.Framework.Entity" /> instances in this instance are initialized.
        /// </remarks>
        protected override void CreateScene()
        {
            //this.CreateVersionLabel();

#if DEBUG
            this.AddSceneBehavior(new DebugSceneBehavior(), SceneBehavior.Order.PreUpdate);
#endif
        }

        private void CreateVersionLabel()
        {
			var releaseType = "RTM";
			var today = "12/02/2014";
			var releaseText = string.Format ("{0} {1}", releaseType, today);

            // RC Version Label
            TextBlock textblock = new TextBlock()
            {
                Width = 1024,
                Height = 20,
				Text = releaseText,
                Foreground = Color.Black,
                TextAlignment = TextAlignment.Right,
                HorizontalAlignment = HorizontalAlignment.Right,
                VerticalAlignment = VerticalAlignment.Bottom,
                DrawOrder = 0f,
            };
            EntityManager.Add(textblock);
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected override void Dispose(bool disposing)
        {
            // Remove Timers
            this.RemoveSceneTimers();

            base.Dispose(disposing);
        }

        /// <summary>
        /// Pauses this instance.
        /// </summary>
        public override void Pause()
        {
            base.Pause();

            foreach (ScopedTimer timer in timers.Values)
            {
                timer.IsPaused = true;
            }

            // Unsubscribe touches
            foreach (Entity entity in EntityManager.EntityGraph)
            {
                // All components
                this.UnsubscribeTouchbles(entity);
            }
        }

        /// <summary>
        /// Resumes this instance.
        /// </summary>
        public override void Resume()
        {
            base.Resume();

            foreach (ScopedTimer timer in timers.Values)
            {
                timer.IsPaused = false;
            }

            // Subscribe touches
            foreach (Entity entity in EntityManager.EntityGraph)
            {
                // All components
                this.SubscribeTouchbles(entity);
            }
        }

        /// <summary>
        /// Removes the scene timers.
        /// </summary>
        public void RemoveSceneTimers()
        {
            for (int i = timers.Count - 1; i >= 0; i--)
            {
                timers.Values.ElementAt(i).Delete();
            }
        }

        /// <summary>
        /// Creates the timer.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="interval">The interval.</param>
        /// <param name="action">The action.</param>
        /// <param name="looped">if set to <c>true</c> [looped].</param>
        /// <returns></returns>
        public ScopedTimer CreateTimer(string name, TimeSpan interval, Action<ScopedTimer> action, bool looped = false)
        {
            ScopedTimer result;

            if (!this.timers.ContainsKey(name))
            {
                result = new ScopedTimer(this, name, interval, action, looped);
                this.timers.Add(name, result);

                Labels.Add("ScopedTimers", this.timers.Count.ToString());

                // When the timer was deleted. Remove it also from the scene internal timers dictionary.

                result.Deleted += (t, o) =>
                    {
                        this.timers.Remove(((ScopedTimer)t).Name);
                        Labels.Add("ScopedTimers", this.timers.Count.ToString());
                    };
            }
            else
            {
                result = this.timers[name];
                result.Update(interval, action, looped);
            }

            return result;
        }

        /// <summary>
        /// Creates the timer.
        /// </summary>
        /// <param name="interval">The interval.</param>
        /// <param name="action">The action.</param>
        /// <param name="looped">if set to <c>true</c> [looped].</param>
        /// <returns></returns>
        public ScopedTimer CreateTimer(TimeSpan interval, Action<ScopedTimer> action, bool looped = false)
        {
            return this.CreateTimer(Guid.NewGuid().ToString(), interval, action, looped);
        }

        /// <summary>
        /// Creates the scoped label.
        /// </summary>
        /// <param name="label">The label.</param>
        /// <param name="text">The text.</param>
        /// <exception cref="System.NotImplementedException"></exception>
        public void CreateScopedLabel(string label, string text)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Registers the task.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="task">The task.</param>
        /// <exception cref="System.NotImplementedException"></exception>
        public void RegisterTask(object key, ITask task)
        {
            this.tasks.Add(key, task);
            Labels.Add("SceneTasks", this.tasks.Count.ToString());

            Action removeTask = () =>
                {
                    this.tasks.Remove(key);
                    Labels.Add("SceneTasks", this.tasks.Count.ToString());
                };

            task.Completed += removeTask;
            task.Cancelled += removeTask;
        }
        /// <summary>
        /// Subscribes the touchbles.
        /// </summary>
        /// <param name="entity">The entity.</param>
        private void SubscribeTouchbles(Entity entity)
        {
            foreach (Component component in entity.Components)
            {
                if (component is ITouchable)
                {
                    this.touchManager.Subscribe(component as ITouchable);
                }
            }

            foreach (Entity child in entity.ChildEntities)
            {
                this.SubscribeTouchbles(child);
            }
        }

        /// <summary>
        /// Unsubscribes the touchbles.
        /// </summary>
        /// <param name="entity">The entity.</param>
        private void UnsubscribeTouchbles(Entity entity)
        {
            foreach (Component component in entity.Components)
            {
                if (component is ITouchable)
                {
                    this.touchManager.Unsubscribe(component as ITouchable);
                }
            }

            foreach (Entity child in entity.ChildEntities)
            {
                this.UnsubscribeTouchbles(child);
            }
        }

        /// <summary>
        /// Gets the task.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        /// <exception cref="System.NotImplementedException"></exception>
        public ITask GetTask(object key)
        {
            if (this.tasks.ContainsKey(key))
                return this.tasks[key];
            else
                return null;
        }
    }
}
